﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using System.Xml;
using System.Xml.XPath;
using ICSharpCode.SharpZipLib.Zip;
using OpenSubtitlesDownloader.Properties;

namespace OpenSubtitlesDownloader {
	public partial class MainForm : Form {
		public MainForm() {
			InitializeComponent();
		}

		private void MainForm_Load(object sender, EventArgs e) {
			mFileNameTextBox.Text = Settings.Default.FileName;
			mHashTextBox.Text = Settings.Default.Hash;
		}

		private void mOpenFileButton_Click(object sender, EventArgs e) {
			mOpenFileDialog.FileName = mFileNameTextBox.Text;
			if (mOpenFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
				mFileNameTextBox.Text = mOpenFileDialog.FileName;
				searchSubs(mFileNameTextBox.Text);
			}
		}

		private async void searchSubs(string fileName) {
			var tMoviehash = MovieHasher.ComputeMovieHash(fileName);
			mHashTextBox.Text = MovieHasher.ToHexadecimal(tMoviehash);
			Settings.Default.Hash = mHashTextBox.Text;
			Settings.Default.FileName = mFileNameTextBox.Text;
			Settings.Default.Save();
			mResponseTextBox.Text = string.Empty;

			var tXmlResponse = await downloadString(string.Format("http://www.opensubtitles.org/en/search/sublanguageid-{1}/moviehash-{0}/xml", mHashTextBox.Text, "eng"));
			if (processResponse(tXmlResponse))
				// Positive result, so return..
				return;

			// No results based on HASH..
			writeLog("No results based on moviehash, so trying moviename instead..");
		
			// Else, try movie name instead..
			tXmlResponse = await downloadString(string.Format("http://www.opensubtitles.org/en/search/sublanguageid-{1}/moviename-{0}/xml", HttpUtility.UrlEncode(Path.GetFileName(fileName)), "eng"));
			processResponse(tXmlResponse);
		}

		private void writeLog(string line) {
			if (mResponseTextBox.Text == string.Empty)
				mResponseTextBox.Text = line;
			else
				mResponseTextBox.Text = string.Format("{0}{1}{2}", mResponseTextBox.Text, Environment.NewLine, line);
		}

		private void mSearchButton_Click(object sender, EventArgs e) {
			searchSubs(mFileNameTextBox.Text);
		}
		private async Task<DownloadResult> downloadString(string uri) {
			writeLog(string.Format("Trying url: {0}", uri));
			DownloadResult tResult = new DownloadResult();
			HttpWebResponse response = null;
			try {
				var request = (HttpWebRequest)HttpWebRequest.Create(uri);
				request.AllowAutoRedirect = false;
				// augment the request here: headers (Referer, User-Agent, etc)
				//     CookieContainer, Accept, etc.
				response = (HttpWebResponse)await request.GetResponseAsync();

				tResult.Status = (int)response.StatusCode;
				if (tResult.Status == 301 || tResult.Status == 302) {
					tResult.Xml = response.Headers["Location"];
				} else {
					var responseEncoding = Encoding.GetEncoding(response.CharacterSet);
					using (var sr = new StreamReader(response.GetResponseStream(), responseEncoding)) {
						tResult.Xml = sr.ReadToEnd();
					}
				}
			} catch (WebException wexc1) {
				// any statusCode other than 200 gets caught here
				if (wexc1.Status == WebExceptionStatus.ProtocolError) {
					// can also get the decription: 
					//  ((HttpWebResponse)wexc1.Response).StatusDescription;
					tResult.Status = (int)((HttpWebResponse)wexc1.Response).StatusCode;
				}
			} finally {
				if (response != null)
					response.Close();
			}
			return tResult;
		}
		private bool processResponse(DownloadResult response) {
			if (response.Status == 200) {
				string tDownloadUrl = null;
				using (var tMemStream = new MemoryStream(Encoding.UTF8.GetBytes(response.Xml))) {
					var tDoc = new XPathDocument(tMemStream);
					var tNavigator = tDoc.CreateNavigator();
					var tMovieNameNodes = tNavigator.Select("/opensubtitles/search/results/subtitle/MovieName");
					if (tMovieNameNodes.MoveNext()) {
						writeLog(tMovieNameNodes.Current.Value);
					} else {
						writeLog("No movie found..");
						return false;
					}
					var tSubtitleNodes = tNavigator.Select("/opensubtitles/search/results/subtitle/IDSubtitle");
					if (tSubtitleNodes.MoveNext()) {
						tDownloadUrl = tSubtitleNodes.Current.GetAttribute("LinkDownload", string.Empty);
						writeLog(tDownloadUrl);
					}
				}
				// process download..
				if (tDownloadUrl == null) {
					writeLog("No download found..");
					return false;
				}
				var tZip = new FastZip();
				try {
					var tRequest = (HttpWebRequest)HttpWebRequest.Create(tDownloadUrl);
					var tZipResponse = (HttpWebResponse)tRequest.GetResponse();
					using (var tStream = tZipResponse.GetResponseStream()) {
						using (var tMemStream = new MemoryStream()) {
							tStream.CopyTo(tMemStream);
							var tTempPath = Path.GetTempPath() + "OpenSubtitlesDownloader";
							Directory.CreateDirectory(tTempPath);
							tZip.ExtractZip(tMemStream, tTempPath, FastZip.Overwrite.Always, null, @"\.srt$", null, false, true);
							var tDirInfo = new DirectoryInfo(tTempPath);
							var tFileInfo = new FileInfo(mFileNameTextBox.Text);

							var tSrtFile = tDirInfo.EnumerateFiles().FirstOrDefault();
							if (tSrtFile == null) {
								writeLog("No .srt file found in zip..");
								return false;
							}
							writeLog("Downloaded and unpacked: " + tSrtFile.Name);
							File.Copy(tSrtFile.FullName, Path.ChangeExtension(mFileNameTextBox.Text, ".srt"), true);
							writeLog("Saved as : " + Path.ChangeExtension(mFileNameTextBox.Text, ".srt"));
							// clean up..
							Directory.Delete(tTempPath, true);
							writeLog("Deleted temp folder.");
							return true;
						}
					}
				} catch (Exception ex) {
					writeLog(ex.Message);
				}
			} else {
				// TODO: parse redirect url and add XML to the end and get subs from there..
				writeLog(string.Format("Didn't get a status 200, but {0} instead.", response.Status));
			}
			return false;
		}
	}
}
